package belf.migrate.engine.job.evaluate;

import belf.migrate.api.job.Job;
import belf.migrate.api.table.schema.AbstractJdbcCatalog;
import belf.migrate.api.taskconf.JobContext;
import belf.migrate.api.type.LogLevelType;
import belf.migrate.api.util.DatabaseType;
import belf.migrate.api.util.PostLog;
import belf.migrate.engine.BelfEvn;
import belf.migrate.engine.job.*;
import belf.migrate.evaluate.helper.*;
import belf.migrate.evaluate.model.*;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;

import java.text.SimpleDateFormat;
import java.util.Date;

import static belf.migrate.api.job.JobCode.*;

/**
 * 迁移前对源、目标数据库进行可能影响迁移过程的事项进行评估，如： 源数据库的特点，包含：表结构、特殊字段；
 * * 视图；索引；外键；类型；特殊字段；是否有自增字段；是否有空间字段；是否有CLOB/BLOB字段等。
 * 根据评估结果决定是否需要做子任务处理。
 */
@Slf4j
public class EvaluateJobRunner extends BaseJobRunner {

    private final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

    private static Gson gson = new Gson();

    public EvaluateJobRunner(Job job) {
        super(job);
    }

    public JobResult check() {
        JobResult jobResult = new JobResult(SUCCESS, "任务执行成功");
        jobResult.setStartTime(simpleDateFormat.format(new Date()));

        jobContext = new JobContext("" + job.getJobId(), taskConf);
        // 创建源端catalog
        AbstractJdbcCatalog SourceCatalog = CatalogCreator.createSourceCatalog(taskConf);
        if (null == SourceCatalog) {
            log.error("No Source Catalog for {}, Stop Job {}!",
                    taskConf.getSourceConf().getCatalogName(),
                    job.getJobId());

            jobResult = new JobResult(NO_SOURCE_CATALOG, "提交Job失败，没有找到目标端" +
                    taskConf.getSinkConf().getCatalogName() +
                    "可适配的Catalog类，请联系开发者。");

            return jobResult;
        }
        switch (job.getTask().getTaskType()) {
            case EVALUATE_TABLE:
            case EVALUATE_VIEW:
            case EVALUATE_TRIGGER:
            case EVALUATE_PROCEDURE:
            case EVALUATE_TYPE:
            case EVALUATE_INDEX:
            case EVALUATE_SEQ:
            case EVALUATE_SYNONYM:
            case EVALUATE_FUNC:
                jobContext.setTotalProgress(1);
                break;
            case EVALUATE_MIGRATION:
                jobContext.setTotalProgress(DatabaseType.getDatabaseType(taskConf.getSourceConf().getCatalogName()).getObjectTypes().size());
                break;


        }
        jobContext.setSourceCatalog(SourceCatalog);
        BelfEvn.getInstance().addJobContext("" + job.getJobId(), jobContext);

        return jobResult;
    }

    @Override
    public JobResult execute() {
        log.info("评估开始！");

        JobResult jobResult = check();
        if (jobResult.getCode() != SUCCESS) {
            return jobResult;
        }
        PostLog.sendLogModel(job.getJobId(), job.getTask().getTaskType().getName(), "评估开始！", LogLevelType.INFO.getType(), EXECUTING,
                jobContext.getProgress());

        EvaluateObjectsResult evaluateObjectsResult = new EvaluateObjectsResult();

        long startTime = System.currentTimeMillis();

        switch (job.getTask().getTaskType()) {

            case EVALUATE_TABLE:
                EvaluateTableResult tableResult = new EvaluateTableTool(jobContext, job).run();
                evaluateObjectsResult.setAllObjectSize(tableResult.getAllObjectSize());
                evaluateObjectsResult.setSinkStatisticsMap(tableResult.getSinkStatisticsMap());
                evaluateObjectsResult.setTableResult(tableResult);
                if (FAILTURE == tableResult.getCode()) {
                    jobResult.setCode(FAILTURE);
                }
                evaluateObjectsResult.setGrammarTypes(tableResult.getGrammarTypes());
                evaluateObjectsResult.setColumnRisk(tableResult.getColumnRisk());
                evaluateObjectsResult.setFunctionRisk(tableResult.getFunctionRisk());
                break;
            case EVALUATE_VIEW:
                EvaluateViewResult viewResult = new EvaluateViewTool(jobContext, job).run();
                evaluateObjectsResult.setAllObjectSize(viewResult.getAllObjectSize());
                evaluateObjectsResult.setSinkStatisticsMap(viewResult.getSinkStatisticsMap());
                evaluateObjectsResult.setViewResult(viewResult);
                if (FAILTURE == viewResult.getCode()) {
                    jobResult.setCode(FAILTURE);
                }
                evaluateObjectsResult.setGrammarTypes(viewResult.getGrammarTypes());
                evaluateObjectsResult.setColumnRisk(viewResult.getColumnRisk());
                evaluateObjectsResult.setFunctionRisk(viewResult.getFunctionRisk());
                break;
            case EVALUATE_TRIGGER:
                EvaluateTriggerResult triggerResult = new EvaluateTriggerTool(jobContext, job).run();
                evaluateObjectsResult.setAllObjectSize(triggerResult.getAllObjectSize());
                evaluateObjectsResult.setSinkStatisticsMap(triggerResult.getSinkStatisticsMap());
                evaluateObjectsResult.setTriggerResult(triggerResult);
                if (FAILTURE == triggerResult.getCode()) {
                    jobResult.setCode(FAILTURE);
                }
                evaluateObjectsResult.setGrammarTypes(triggerResult.getGrammarTypes());
                evaluateObjectsResult.setColumnRisk(triggerResult.getColumnRisk());
                evaluateObjectsResult.setFunctionRisk(triggerResult.getFunctionRisk());
                break;
            case EVALUATE_PROCEDURE:
                EvaluateProcedureResult proResult = new EvaluateProcedureTool(jobContext, job).run();
                evaluateObjectsResult.setAllObjectSize(proResult.getAllObjectSize());
                evaluateObjectsResult.setSinkStatisticsMap(proResult.getSinkStatisticsMap());
                evaluateObjectsResult.setProcedureResult(proResult);
                if (FAILTURE == proResult.getCode()) {
                    jobResult.setCode(FAILTURE);
                }
                evaluateObjectsResult.setGrammarTypes(proResult.getGrammarTypes());
                evaluateObjectsResult.setColumnRisk(proResult.getColumnRisk());
                evaluateObjectsResult.setFunctionRisk(proResult.getFunctionRisk());
                break;
            case EVALUATE_TYPE:
                EvaluateTypeResult typeResult = new EvaluateTypeTool(jobContext, job).run();
                evaluateObjectsResult.setAllObjectSize(typeResult.getAllObjectSize());
                evaluateObjectsResult.setSinkStatisticsMap(typeResult.getSinkStatisticsMap());
                evaluateObjectsResult.setTypeResult(typeResult);
                if (FAILTURE == typeResult.getCode()) {
                    jobResult.setCode(FAILTURE);
                }
                evaluateObjectsResult.setGrammarTypes(typeResult.getGrammarTypes());
                evaluateObjectsResult.setColumnRisk(typeResult.getColumnRisk());
                evaluateObjectsResult.setFunctionRisk(typeResult.getFunctionRisk());
                break;
            case EVALUATE_INDEX:
                EvaluateIndexResult indexResult = new EvaluateIndexTool(jobContext, job).run();
                evaluateObjectsResult.setAllObjectSize(indexResult.getAllObjectSize());
                evaluateObjectsResult.setSinkStatisticsMap(indexResult.getSinkStatisticsMap());
                evaluateObjectsResult.setIndexResult(indexResult);
                if (FAILTURE == indexResult.getCode()) {
                    jobResult.setCode(FAILTURE);
                }
                evaluateObjectsResult.addIndexRisk(indexResult.getIndexRisk());
                break;
            case EVALUATE_SEQ:
                EvaluateSeqResult seqResult = new EvaluateSeqTool(jobContext, job).run();
                evaluateObjectsResult.setAllObjectSize(seqResult.getAllObjectSize());
                evaluateObjectsResult.setSinkStatisticsMap(seqResult.getSinkStatisticsMap());
                evaluateObjectsResult.setSeqResult(seqResult);
                if (FAILTURE == seqResult.getCode()) {
                    jobResult.setCode(FAILTURE);
                }
                evaluateObjectsResult.setGrammarTypes(seqResult.getGrammarTypes());
                evaluateObjectsResult.setColumnRisk(seqResult.getColumnRisk());
                evaluateObjectsResult.setFunctionRisk(seqResult.getFunctionRisk());
                break;
            case EVALUATE_SYNONYM:
                EvaluateSynonymResult synonymResult = new EvaluateSynonymTool(jobContext, job).run();
                evaluateObjectsResult.setAllObjectSize(synonymResult.getAllObjectSize());
                evaluateObjectsResult.setSinkStatisticsMap(synonymResult.getSinkStatisticsMap());
                evaluateObjectsResult.setSynonymResult(synonymResult);
                if (FAILTURE == synonymResult.getCode()) {
                    jobResult.setCode(FAILTURE);
                }
                evaluateObjectsResult.setGrammarTypes(synonymResult.getGrammarTypes());
                evaluateObjectsResult.setColumnRisk(synonymResult.getColumnRisk());
                evaluateObjectsResult.setFunctionRisk(synonymResult.getFunctionRisk());
                break;
            case EVALUATE_FUNC:
                EvaluateFunctionResult functionResult = new EvaluateFunctionTool(jobContext, job).run();
                evaluateObjectsResult.setAllObjectSize(functionResult.getAllObjectSize());
                evaluateObjectsResult.setSinkStatisticsMap(functionResult.getSinkStatisticsMap());
                evaluateObjectsResult.setFunctionResult(functionResult);
                if (FAILTURE == functionResult.getCode()) {
                    jobResult.setCode(FAILTURE);
                }
                evaluateObjectsResult.setGrammarTypes(functionResult.getGrammarTypes());
                evaluateObjectsResult.setColumnRisk(functionResult.getColumnRisk());
                evaluateObjectsResult.setFunctionRisk(functionResult.getFunctionRisk());
                break;
            case EVALUATE_MIGRATION:
                EvaluateObjectsTool evaluateObjectsTool = new EvaluateObjectsTool(jobContext, job);
                evaluateObjectsResult = evaluateObjectsTool.run();
                if (FAILTURE == evaluateObjectsResult.getCode()) {
                    jobResult.setCode(FAILTURE);
                }
                break;
            default:
                break;
        }
        long endTime = System.currentTimeMillis();
        String timeConsuming = String.valueOf((double) (endTime - startTime) / 1000);
        evaluateObjectsResult.setTimeConsuming(String.format("%s秒", timeConsuming));

        // 语法评估和整体评估
        EvaluateResult evaluateResult = new EvaluateTool(jobContext, evaluateObjectsResult).run();
        endTime = System.currentTimeMillis();
        timeConsuming = String.valueOf((double) (endTime - startTime) / 1000);
        evaluateResult.setTimeConsuming(String.format("%s秒", timeConsuming));

        jobResult.setData(evaluateResult);
        jobResult.setJobId(job.getJobId());
        //释放数据库连接
        jobContext.release();
        log.info("评估结束！");
        PostLog.sendLogModel(job.getJobId(), job.getTask().getTaskType().getName(), "评估结束！", LogLevelType.INFO.getType(), EXECUTING,
                jobContext.getProgress());

        jobResult.setEndTime(simpleDateFormat.format(new Date()));
        jobResult.setTimeConsuming(timeConsuming);
        return jobResult;
    }
}
